%{
open Ast
%}

/* 1. Token定义 */
%token <int> T_INT
%token <string> T_ID
%token <Ast.ty> T_TY
%token <Ast.unary_op> T_UNARY
%token T_ADD T_SUB T_MUL T_DIV T_MOD
%token T_LT T_GT T_LE T_GE T_EQ T_NE T_AND T_OR
%token T_LPAREN T_RPAREN
%token T_LBRACE T_RBRACE T_SEMI T_COMMA
%token T_ASSIGN T_IF T_ELSE T_WHILE
%token T_BREAK T_CONTINUE T_RETURN T_EOF

/* 2. 起始符号声明 */
%start program
%type <Ast.program> program

/* 3. 运算符优先级和结合性 */
%right T_ASSIGN
%right T_UNARY
%left T_OR
%left T_AND
%left T_EQ T_NE
%left T_LT T_LE T_GT T_GE
%left T_ADD T_SUB
%left T_MUL T_DIV T_MOD


/* 4. 语法规则部分 */
%%

program:
  | func_def_list T_EOF { $1 }  /* 只在最后出现一个T_EOF */

func_def_list:
  | func_def { [$1] }          /* 单个函数定义 */
  | func_def_list func_def { $1 @ [$2] }  /* 多个函数定义 */

func_def:
  | T_TY T_ID T_LPAREN params T_RPAREN T_LBRACE stmt_list T_RBRACE
    { { ret_ty = $1; name = $2; params = $4; body = Block($7) } }

params:
  | { [] }
  | param_list { $1 }

param_list:
  | param { [$1] }
  | param_list T_COMMA param { $1 @ [$3] }

param:
  | T_TY T_ID { { pty = $1; pid = $2 } }

stmt_list:
  | { [] }
  | stmt_list stmt { $1 @ [$2] }

stmt:
  | T_SEMI { Empty }
  | expr T_SEMI { ExprStmt $1 }
  | T_TY T_ID T_SEMI { Declare($2, Num(0)) }
  | T_TY T_ID T_ASSIGN expr T_SEMI { Declare($2, $4) }
  | T_ID T_ASSIGN expr T_SEMI { Assign($1, $3) }
  | T_IF T_LPAREN expr T_RPAREN stmt  { If($3, $5, None) }
  | T_IF T_LPAREN expr T_RPAREN stmt T_ELSE stmt { If($3, $5, Some $7) }
  | T_WHILE T_LPAREN expr T_RPAREN stmt { While($3, $5) }
  | T_BREAK T_SEMI { Break }
  | T_CONTINUE T_SEMI { Continue }
  | T_RETURN expr T_SEMI { Return(Some $2) }
  | T_RETURN T_SEMI { Return None }
  | T_LBRACE stmt_list T_RBRACE { Block $2 }

expr:
  | T_INT { Num $1 }
  | T_ID { ID $1 }
  | T_LPAREN expr T_RPAREN { Paren $2 }
  | T_ADD expr %prec T_UNARY { Unary(UPlus, $2) }    (* 一元正号 *)
  | T_SUB expr %prec T_UNARY { Unary(UMinus, $2) }  (* 一元负号 *)
  | T_UNARY expr %prec T_UNARY { Unary($1, $2) }     (* 逻辑非 *)
  | expr T_ADD expr { Binary(Add, $1, $3) }
  | expr T_SUB expr { Binary(Sub, $1, $3) }
  | expr T_MUL expr { Binary(Mul, $1, $3) }
  | expr T_DIV expr { Binary(Div, $1, $3) }
  | expr T_MOD expr { Binary(Mod, $1, $3) }
  | expr T_LT expr { Binary(LT, $1, $3) }
  | expr T_GT expr { Binary(GT, $1, $3) }
  | expr T_LE expr { Binary(LE, $1, $3) }
  | expr T_GE expr { Binary(GE, $1, $3) }
  | expr T_EQ expr { Binary(EQ, $1, $3) }
  | expr T_NE expr { Binary(NE, $1, $3) }
  | expr T_AND expr { Binary(And, $1, $3) }
  | expr T_OR expr { Binary(Or, $1, $3) }
  | T_ID T_LPAREN args T_RPAREN { Call($1, $3) }

args:
  | { [] }
  | expr_list { $1 }

expr_list:
  | expr { [$1] }
  | expr_list T_COMMA expr { $1 @ [$3] }